package Semantics;

import java.util.ArrayList;

import SyntaxTree.ASTMyID;
import SyntaxTree.ASTMyKeyword;
import SyntaxTree.ASTprogram;
import SyntaxTree.Node;
import SyntaxTree.SimpleNode;
import SyntaxTree.XYZ2parserTreeConstants;

public class Semantics {	
	private ArrayList<SemanticsException> exceptions = new ArrayList<SemanticsException>();
	private ArrayList<SemanticsOverload> overloads = new ArrayList<SemanticsOverload>();
	private ArrayList<VarSymbol> vartable = new ArrayList<VarSymbol>();
	
	public ArrayList<VarSymbol> getVartable() {
		return vartable;
	}

	public void setVartable(ArrayList<VarSymbol> vartable) {
		this.vartable = vartable;
	}

	public ArrayList<SemanticsException> getExceptions(){
		return exceptions;
	}
	
	public ArrayList<SemanticsOverload> getOverloads(){
		return overloads;
	}
	
	public  VarSymbol[] GetParams(SimpleNode formallist, Table table){
		Node[] children = formallist.getChildren();
		int sz = children.length;
		VarSymbol[] params;
		int numOfParams = 1;
		while(((SimpleNode)children[--sz]).getId() == XYZ2parserTreeConstants.JJTFORMREST)
			numOfParams += 1;
		params = new VarSymbol[numOfParams];
		String[] tandi = GetTypeAndID((SimpleNode)formallist,table);
		params[0] = new VarSymbol();
		params[0].setType(tandi[0]);
		params[0].setId(tandi[1]);
		params[0].setJJTType(XYZ2parserTreeConstants.JJTVARDECL);
		
		sz = children.length;
		
		while(((SimpleNode)children[--sz]).getId() == XYZ2parserTreeConstants.JJTFORMREST){
			tandi = GetTypeAndID((SimpleNode)children[sz],table);
			params[--numOfParams] = new VarSymbol();
			params[numOfParams].setType(tandi[0]);
			params[numOfParams].setId(tandi[1]);
			params[numOfParams].setLine(((SimpleNode)children[sz]).jjtGetLine());
			params[numOfParams].setJJTType(XYZ2parserTreeConstants.JJTVARDECL);
		}
		
		for(int i = 0; i < params.length; i++)
			vartable.add(params[i]);
		
		return params;
	}
	
	public  VarSymbol[] GetParams(SimpleNode formallist){
		Node[] children = formallist.getChildren();
		int sz = children.length;
		VarSymbol[] params;
		int numOfParams = 1;
		while(((SimpleNode)children[--sz]).getId() == XYZ2parserTreeConstants.JJTFORMREST)
			numOfParams += 1;
		params = new VarSymbol[numOfParams];
		String[] tandi = GetTypeAndID((SimpleNode)formallist);
		params[0] = new VarSymbol();
		params[0].setType(tandi[0]);
		params[0].setId(tandi[1]);
		params[0].setJJTType(XYZ2parserTreeConstants.JJTVARDECL);
		
		sz = children.length;
		
		while(((SimpleNode)children[--sz]).getId() == XYZ2parserTreeConstants.JJTFORMREST){
			tandi = GetTypeAndID((SimpleNode)children[sz]);
			params[--numOfParams] = new VarSymbol();
			params[numOfParams].setType(tandi[0]);
			params[numOfParams].setId(tandi[1]);
			params[numOfParams].setLine(((SimpleNode)children[sz]).jjtGetLine());
			params[numOfParams].setJJTType(XYZ2parserTreeConstants.JJTVARDECL);
		}
		
		for(int i = 0; i < params.length; i++)
			vartable.add(params[i]);
		
		return params;
	}
	
	public String[] GetTypeAndID(SimpleNode parent, Table table){	 
		String[] res = new String[2];
		Node[] children = parent.getChildren();
		SimpleNode type = (SimpleNode)((SimpleNode)children[0]).jjtGetChild(0);
		if(type.getId() == XYZ2parserTreeConstants.JJTMYKEYWORD){	// type is a keyword
			res[0] = ((ASTMyKeyword)type).getName();
			res[1] = ((ASTMyID)children[1]).getName();
		}
		else{
			ASTMyID ast = (ASTMyID)type;
			res[0] = ast.getName();
			res[1] = ((ASTMyID)children[1]).getName();
			
			ClassSymbol cs = table.getClassSymbol(ast.getName());
			if(cs == null){
				exceptions.add(new SemanticsException(SemanticsException.TYPE_UNDEFINED,
								res[0],ast.jjtGetLine()));
			}
		}	
		return res;
	}
	
	public String[] GetTypeAndID(SimpleNode parent){	 
		String[] res = new String[2];
		Node[] children = parent.getChildren();
		SimpleNode type = (SimpleNode)((SimpleNode)children[0]).jjtGetChild(0);
		if(type.getId() == XYZ2parserTreeConstants.JJTMYKEYWORD){	// type is a keyword
			res[0] = ((ASTMyKeyword)type).getName();
			res[1] = ((ASTMyID)children[1]).getName();
		}
		else{
			ASTMyID ast = (ASTMyID)type;
			res[0] = ast.getName();
			res[1] = ((ASTMyID)children[1]).getName();
		}	
		return res;
	}
	// first scan check extends
	public Table GetClassTable(ASTprogram parsertree){		
		ArrayList<ClassSymbol> extendsAr = new ArrayList<ClassSymbol>();
		
		Node[] children = parsertree.getChildren();
		Table table = new Table();
		
		for(int i = 2; i < children.length; i += 2){
			ClassSymbol cs = new ClassSymbol();
			SimpleNode n = (SimpleNode)children[i];
			String id = ((ASTMyID)n.jjtGetChild(0)).getName(); 
			cs.setId(id);
			
			SimpleNode su = (SimpleNode)n.jjtGetChild(1);
			if(su.getId() == XYZ2parserTreeConstants.JJTMYID){
				String suname = ((ASTMyID)n.jjtGetChild(1)).getName();
				cs.setSuper(suname);// need to check this super
				extendsAr.add(cs);
			}
			cs.setJJTType(XYZ2parserTreeConstants.JJTCLASSDECL);
			cs.setLine(children[i].jjtGetLine());
			
			if(table.addSymbol(cs) == Table.DUPLICATE){
				exceptions.add(new SemanticsException(SemanticsException.CLASS_REDEFINITION,
						cs.getId(),cs.getLine()));
			}
			
		}
		
		//check if all supers exist
		ArrayList<String> added = new ArrayList<String>();
		for(int i = 0; i < extendsAr.size(); ++i){
			ClassSymbol cs = extendsAr.get(i);
			ClassSymbol scs = table.getClassSymbol(cs.getSuper());
			
			if( scs == null){
				exceptions.add(new SemanticsException(SemanticsException.SUPERCLASS_NOTEXISTS,
						cs.getSuper(),cs.getLine()));
			}
			else{
				//检测循环 继承
				ArrayList<String> ids = new ArrayList<String>();
				ArrayList<Integer> lines = new ArrayList<Integer>();
				
				while(scs != null){
					ids.add(scs.getId());
					lines.add(scs.getLine());
					if(scs.getId().equals(cs.getId())){
						//TODO exception 循环依赖
						String[] strIds = new String[ids.size()];
						int[] ilines = new int[ids.size()];
						
						for(int index = 0; index < ids.size(); ++index){
							strIds[index] = ids.get(index);
							ilines[index] = lines.get(index);	
						}
						
						boolean newrecursive = true;
						for(int z = 0; z < added.size(); ++z){
							if(added.get(z).equals(cs.getId())){
								newrecursive = false;
								break;
							}
						}
						
						if(newrecursive){
							exceptions.add(new SemanticsException(
									SemanticsException.RECURSIVE_EXTENDS,
									strIds,ilines));
							for(int n = 0; n <  strIds.length; ++n){
								added.add(strIds[n]);
							}
						}
						break;
					}
					else{
						if(scs.getSuper() == null){
							scs = null;
						}
						else{
							scs = table.getClassSymbol(scs.getSuper());
						}
					}	
				}
			}
		}
		return table;
	}
	
	public Table GetVarAndMethodFromClass(SimpleNode csn, Table table){  
		String id = ((ASTMyID)csn.jjtGetChild(0)).getName();
		ClassSymbol cs = table.getClassSymbol(id);
		ArrayList<String> strOverloads = new ArrayList<String>();
		ArrayList<String> varRedefination = new ArrayList<String>();
		
		int start = 2;
		SimpleNode su = (SimpleNode)csn.jjtGetChild(1);
		if(su.getId() == XYZ2parserTreeConstants.JJTMYID){			// extends
			start = 3;
		}
		
		Node[] children = csn.getChildren();
		for(int i = start; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];
			
			if(node.getId() == XYZ2parserTreeConstants.JJTMETHODDECL){
				MethodSymbol ms = GetMethodSymbolFromNode(node,table);
				//TODO 检查类里面的函数是否被重载 或者 多次定义
				Object[] methods = cs.getAllMethod(ms.getId());
				if(methods == null){
					cs.addMethod(ms);
				}
				else{
					boolean addtomethods = true;
					for(int j = 0; j < methods.length; ++j){
						MethodSymbol method = (MethodSymbol)methods[j];
						int res = CompareMethods(ms,method);
						if(res != 1){	//完全相同或者只有返回类型不同 不加入methodhash
							exceptions.add(new SemanticsException(SemanticsException.METHOD_REDEFINITION,
									ms.getId(),ms.getLine()));
							addtomethods = false;
							break;
						}
					}
					//重载函数
					if(addtomethods){
						cs.addMethod(ms);
						boolean newoverloads = true;
						for(int z = 0; z < strOverloads.size(); ++z){
							if(strOverloads.get(z).equals(ms.id)){
								newoverloads = false;
								break;
							}
						}
						if(newoverloads)
							strOverloads.add(ms.getId());
					}
				}
			}
			else if(node.getId() == XYZ2parserTreeConstants.JJTVARDECL){
				VarSymbol vs = GetVarSymbolFromNode(node,table);
				//add var global in the class 
					vartable.add(vs);
					
				//TODO 检查变量是否有重复定义
				boolean redefine = false;
				for(int j = 0; j < varRedefination.size(); ++j){
					if(varRedefination.get(j).equals(vs.getId())){
						redefine = true;
						exceptions.add(new SemanticsException(SemanticsException.VAR_REDEFINITION,
								vs.getId(),vs.getLine()));
						break;
					}
				}
				if(redefine == false)
					cs.addVarSymbol(vs);
			}
		}
		//将所有的重载函数加入到 重载函数表中

		for(int i = 0; i < strOverloads.size(); ++i){
			Object[] methods = cs.getAllMethod(strOverloads.get(i));
			overloads.add(new SemanticsOverload(methods));
		}

		return table;
	}
	
	public MethodSymbol GetMethodSymbolFromNode(SimpleNode node,Table table){
		Node[] children = node.getChildren();
		MethodSymbol ms = new MethodSymbol();
		Node[] grandson_type = ((SimpleNode)children[0]).getChildren();
		Node grandson_id = ((SimpleNode)children[1]);
		SimpleNode formallist;
		if(((SimpleNode)grandson_type[0]).getId() == XYZ2parserTreeConstants.JJTMYKEYWORD){	// type is a keyword
			ms.setType(((ASTMyKeyword)grandson_type[0]).getName());
			ms.setId(((ASTMyID)grandson_id).getName());
			ms.setLine(children[0].jjtGetLine());
			formallist = (SimpleNode)children[3];
		}
		else{
			//ASTMyID ast = (ASTMyID)((SimpleNode)children[0]).jjtGetChild(0);
			//ms.setType(ast.getName());
			ms.setType(((ASTMyID)grandson_type[0]).getName());
			ms.setId(((ASTMyID)grandson_id).getName());
			ms.setLine(children[0].jjtGetLine());
			//check if the return type has been declared
			ClassSymbol cs = table.getClassSymbol(((ASTMyID)children[1]).getName());
			if(cs == null){
				exceptions.add(new SemanticsException(SemanticsException.RETURNTYPE_UNDEFINED,
						((ASTMyID)grandson_id).getName(),node.jjtGetLine()));
			}
			formallist = (SimpleNode)children[3];	
		}
		
		VarSymbol[] params = GetParams(formallist,table);
		ms.setParams(params);
		return ms;
	}
	
	public MethodSymbol GetMethodSymbolFromNode(SimpleNode node){
		Node[] children = node.getChildren();
		MethodSymbol ms = new MethodSymbol();
		Node[] grandson_type = ((SimpleNode)children[0]).getChildren();
		Node grandson_id = ((SimpleNode)children[1]);
		SimpleNode formallist;
		if(((SimpleNode)grandson_type[0]).getId() == XYZ2parserTreeConstants.JJTMYKEYWORD){	// type is a keyword
			ms.setType(((ASTMyKeyword)grandson_type[0]).getName());
			ms.setId(((ASTMyID)grandson_id).getName());
			ms.setLine(children[0].jjtGetLine());
			formallist = (SimpleNode)children[3];
		}
		else{
			//ASTMyID ast = (ASTMyID)((SimpleNode)children[0]).jjtGetChild(0);
			//ms.setType(ast.getName());
			ms.setType(((ASTMyID)grandson_type[0]).getName());
			ms.setId(((ASTMyID)grandson_id).getName());
			ms.setLine(children[0].jjtGetLine());
			//check if the return type has been declared
			formallist = (SimpleNode)children[3];	
		}
		
		VarSymbol[] params = GetParams(formallist);
		ms.setParams(params);
		return ms;
	}
	
	public VarSymbol GetVarSymbolFromNode(SimpleNode node,Table table){
		String[] var = GetTypeAndID(node,table);
		VarSymbol s = new VarSymbol();
		s.setId(var[1]);
		s.setType(var[0]);
		s.setLine(node.jjtGetLine());
		s.setJJTType(XYZ2parserTreeConstants.JJTVARDECL);
		//vartable.add(s);
		return s;
	}
	
	 
	
	
	public Table GetGlobalTable(ASTprogram parsertree){	// second scan
		Table table = GetClassTable(parsertree);
		
		Node[] children = parsertree.getChildren();		
		for(int i = 2; i < children.length; i += 2){
			SimpleNode node = (SimpleNode)children[i];
			table = GetVarAndMethodFromClass(node,table);
		}
		//第二遍扫描结束，检查类之间的函数继承关系
		Object[] classes = table.getAllClass();
		for(int i = 0; i < classes.length; ++i){
			ClassSymbol cs = (ClassSymbol)classes[i];
			ArrayList<String> strOverloads = new ArrayList<String>();
			
			if(cs.getSuper().length() == 0)
				continue;
			
			ClassSymbol scs = table.getClassSymbol(cs.getSuper());
			
			while(scs != null && scs.getId().equals(cs.getId()) == false){
				Object[] methods = scs.getAllMethod();
				
				for(int j = 0; j < methods.length; ++j){
					MethodSymbol ms = (MethodSymbol)methods[j];
					Object[] curms = cs.getAllMethod(ms.getId());
					if(curms == null){
						cs.addMethod(ms);
						continue;
					}
					
					boolean newmethod = true;
					for(int z = 0; z < curms.length; ++z){
						MethodSymbol temp = (MethodSymbol)curms[z]; 
						int res = CompareMethods(ms, (MethodSymbol)curms[z]);
						if(res == 3){	//只有返回类型不同
							exceptions.add(new SemanticsException(SemanticsException.METHOD_REDEFINITION,
									temp.getId(),temp.getLine()));
							newmethod = false;
							break;
						}
						else if(res == 2){ // 完全相同
							newmethod = false;
							break;
						}
					}
					if(newmethod){
						cs.addMethod(ms);
						boolean newoverload = true;
						for(int m = 0; m < strOverloads.size(); ++m){
							if(strOverloads.get(m).equals(ms.getId())){
								newoverload = false;
							}
						}
						if(newoverload)
							strOverloads.add(ms.getId());		
					}	
				}
				
				for(int index = 0; index < strOverloads.size(); ++index){
					Object[] oms = cs.getAllMethod(strOverloads.get(index));
					overloads.add(new SemanticsOverload(SemanticsOverload.OVERLOAD_SUPERCLASS,
							oms));
				}
				scs = table.getClassSymbol(scs.getSuper());
			}
			
			
		}
		return table;
	}
	
	public void TypeCheckTest(ASTprogram parsertree){  // third scan only check method
		Table table = GetGlobalTable(parsertree);
		table.printTable();
		
		Node[] children = parsertree.getChildren();
		
		MainClassHandler((SimpleNode)children[0],table);
		
		for(int i = 2; i < children.length; i += 2){
			NormalClassHandler((SimpleNode)children[i],table);
		}
		return;	
	}
	
	public void MainClassHandler(SimpleNode mainNode, Table table){	//finished
		ArrayList<String> vars = new ArrayList<String>();
		Node[] children = mainNode.getChildren();
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i]; 
			if(node.getId() == XYZ2parserTreeConstants.JJTSTATEMENT){
				VarSymbol var = StmtHandler(node,table);
				if(var != null){
					PushVarIntoTable(var,vars,table);
				}
			}
		}
		PopVarsFromTable(vars,table);
	}
	
	public void NormalClassHandler(SimpleNode classNode, Table table){	// finished
		String id = ((ASTMyID)classNode.jjtGetChild(0)).getName();
		ClassSymbol cs = table.getClassSymbol(id);
		table.setVarHash(cs.getVarHash());
		table.setMethodHash(cs.getMethodHash());
		
		Node[] children = classNode.getChildren();
		for(int i = 0; i < children.length; ++i){
			SimpleNode n = (SimpleNode)children[i];
			if(n.getId() == XYZ2parserTreeConstants.JJTMETHODDECL){
				MethodHandler(n,table);
			}
		}
		table.clearVarHash();
		table.clearMethodHash();
	}
	
	public void MethodHandler(SimpleNode methodNode, Table table){
		String id = GetMethodNameFromNode(methodNode);
		Object[] methods = table.getAllMethods(id);
		MethodSymbol msFromNode = GetMethodSymbolFromNode(methodNode); //这里不需要检查该函数的类型是否正确 
		
		MethodSymbol ms = null;
		for(int i = 0; i < methods.length; ++i){
			if(CompareMethods((MethodSymbol)methods[i],msFromNode) == 2){ // 匹配
				ms = msFromNode;
				break;
			}
		}
		if(ms == null)
			return;
		
		VarSymbol[] params = ms.getParams();
		
		ArrayList<String> vars = new ArrayList<String>();	//参数中不可能出现相同名称的变量,不需要检查		
		for(int i = 0; i < params.length; ++i){
			vars.add(params[i].getId());
			table.addSymbol(params[i]);
		}
		
		Node[]	children = methodNode.getChildren();
		SimpleNode postNode = null;
		boolean hasReturnStmt = false;
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i]; 
			
			switch(node.getId()){
			case XYZ2parserTreeConstants.JJTPREDECL:{	 
				if(!PreHandler(node,table).equals("boolean")){
					exceptions.add(new SemanticsException(SemanticsException.PREDECL_ERROR,
							"pre statement illegal",node.jjtGetLine()));
				}
			}
				break;
			case XYZ2parserTreeConstants.JJTPOSTDECL:{ 
				postNode = node;
			}
				break;
			case XYZ2parserTreeConstants.JJTSTATEMENT:{
				VarSymbol var = StmtHandler(node,table);
				if(var != null){
					if(var.getId() != null && var.getId().length() > 0){ //子节点为 VarDecl
						
						// var table add 
						//vartable.add(var);
						
						PushVarIntoTable(var,vars,table);
					}
					else{	//字节点为return，判断返回类型是否正确
						// if there is one return statement in the end of the method
						if(i == children.length-1)
							hasReturnStmt = true;
						if(i < children.length-1 && i >=0)
							exceptions.add(new SemanticsException(SemanticsException.RETURN_EARLY_ERROR,
									"return too early",children[i].jjtGetLine()));
						if(CanT1BeT2(ms.getType(),var.getType(),table) == false){
							exceptions.add(new SemanticsException(SemanticsException.RETURN_TYPE_ERROR,
									var.getType(),node.jjtGetLine()));
						}
					}
					
				}
			}
				break;
			}
		}
		// for no return handler
		if(!hasReturnStmt)
			exceptions.add(new SemanticsException(SemanticsException.NO_RETURN_ERROR,
					"no return statement",children[children.length-1].jjtGetLine()));
		if(!PostHandler(postNode,table).equals("boolean")){
			exceptions.add(new SemanticsException(SemanticsException.POSTDECL_ERROR,
					"post statement illegal",postNode.jjtGetLine()));
		}
		
		PopVarsFromTable(vars,table);
		return;
	}
	
	public VarSymbol StmtHandler(SimpleNode stmtNode, Table table){
		SimpleNode child = (SimpleNode)stmtNode.jjtGetChild(0);
		switch(child.getId()){
		case XYZ2parserTreeConstants.JJTVARDECL:{
			//vartable.add(this.GetVarSymbolFromNode(child, table));
			return VarStmtHandler(child,table);
		}
			
		case XYZ2parserTreeConstants.JJTBLOCK:{
			return BlockStmtHandler(child,table);
		}
		case XYZ2parserTreeConstants.JJTPRINTSTATEMENT:{
			PrintStmtHandler(child,table);
			return null;
		}
		case XYZ2parserTreeConstants.JJTASSIGNMENTSTATEMENT:{
			AssignStmtHandler(child,table);
			return null;
		}
		case XYZ2parserTreeConstants.JJTARRAYASSIGNMENTSTATEMENT:{
			ArrayAssignStmtHandler(child,table);
			return null;
		}
		case XYZ2parserTreeConstants.JJTIFSTATEMENT:{
			return IfStmtHandler(child,table);
		}
		
		case XYZ2parserTreeConstants.JJTWHILESTATEMENT:{
			return WhileStmtHandler(child,table);
		}
		
		case XYZ2parserTreeConstants.JJTRETURNSTATEMENT:{
			return ReturnStmtHandler(child,table);
		
		}
		}
		return null;
	}
	
	
	
	public void PushVarIntoTable(VarSymbol var, ArrayList<String> vars, Table table){
		//vartable.add(var);
		for(int i = 0; i < vars.size(); ++i){			// check if var has been defined
			if(vars.get(i).equals(var.getId())){
				// add to exception	  redefination of local vars
				exceptions.add(new SemanticsException(SemanticsException.VAR_REDEFINITION,
												var.getId(),var.getLine()));	
				
				return;
			}
		}
		
		vars.add(var.getId());
		table.addSymbol(var);
	}
	
	public void PopVarsFromTable(ArrayList<String>vars, Table table){
		for(int i = 0; i < vars.size(); ++i)
			table.deleteVarSymbol(vars.get(i));
	}
	
	public String GetMethodNameFromNode(SimpleNode mn){
		Node[] children = mn.getChildren();
		if(((SimpleNode)children[0]).getChildren() == null){	// type is a keyword
			return ((ASTMyID)children[2]).getName();
		}
		else{
			return ((ASTMyID)children[1]).getName();	
		}
	}
	
	public String PreHandler(SimpleNode preNode, Table table){
		Node[] children = preNode.getChildren();
		SimpleNode child = (SimpleNode)children[1];
		return ExpHandler(child,table);
	}
	
	public String PostHandler(SimpleNode PostNode, Table table){
		Node[] children = PostNode.getChildren();
		SimpleNode child = (SimpleNode)children[1];
		return ExpHandler(child,table);
	}
	
	public VarSymbol VarStmtHandler(SimpleNode varNode, Table table){
		vartable.add(GetVarSymbolFromNode(varNode,table));
		return GetVarSymbolFromNode(varNode,table);
	}
 
	public VarSymbol BlockStmtHandler(SimpleNode blockNode, Table table){
		Node[] children = blockNode.getChildren();
		ArrayList<String> vars = new ArrayList<String>();
		String return_type = null;
		
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTSTATEMENT:{
					VarSymbol var = StmtHandler(node,table);
					if(var != null){
						if(var.getId() != null && var.getId().length() > 0){ //子节点为 VarDecl
							PushVarIntoTable(var,vars,table);
						}
						else{	//字节点为return，判断返回类型是否正确
							if(return_type==null)
								return_type = var.getType();
							else
							{
								if(!return_type.equals(var.getType())){
									exceptions.add(new SemanticsException(SemanticsException.RETURN_TYPE_ERROR,
											"Different Return Type",node.jjtGetLine()));
								}
							}
						}
					}
				}
				break;
			}
		}
		
		PopVarsFromTable(vars,table);
		if (return_type == null)
			return null;
		VarSymbol result = new VarSymbol();
		result.setType(return_type);
		result.setId("");
		result.setLine(blockNode.jjtGetLine());
		result.setJJTType(XYZ2parserTreeConstants.JJTRETURNSTATEMENT);
		return result;
	}
	
	public VarSymbol IfStmtHandler(SimpleNode ifNode, Table table){
		Node[] children = ifNode.getChildren();
		
		String return_type = null;
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTEXPRESSION:{	 
					if(!ExpHandler(node,table).equals("boolean")){
						exceptions.add(new SemanticsException(SemanticsException.CONDITION_ERROR,
								"if condition illegal",node.jjtGetLine()));
					}
				}
				break;
				case XYZ2parserTreeConstants.JJTSTATEMENT:{
					ArrayList<String> vars = new ArrayList<String>();
					VarSymbol var = StmtHandler(node,table);
					if(var != null){
						if(var.getId() != null && var.getId().length() > 0){ //子节点为 VarDecl
							PushVarIntoTable(var,vars,table);
						}
						else{	//字节点为return，判断返回类型是否正确
							if(return_type==null)
								return_type = var.getType();
							else
							{
								if(!return_type.equals(var.getType())){
									exceptions.add(new SemanticsException(SemanticsException.RETURN_TYPE_ERROR,
											"Different Return Type",node.jjtGetLine()));
								}
							}
						}
					}
					PopVarsFromTable(vars,table);
				}
				break;
			}
		}
		
		if (return_type == null)
			return null;
		VarSymbol result = new VarSymbol();
		result.setType(return_type);
		result.setId("");
		result.setLine(ifNode.jjtGetLine());
		result.setJJTType(XYZ2parserTreeConstants.JJTRETURNSTATEMENT);
		return result;
	}
	
	public VarSymbol WhileStmtHandler(SimpleNode whileNode, Table table){
		Node[] children = whileNode.getChildren();
		String return_type = null;
		ArrayList<String> vars = new ArrayList<String>();
		
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTEXPRESSION:{	 
					if(!ExpHandler(node,table).equals("boolean")){
						exceptions.add(new SemanticsException(SemanticsException.CONDITION_ERROR,
								"while condition",node.jjtGetLine()));
					}
				}
				break;
				case XYZ2parserTreeConstants.JJTSTATEMENT:{
					VarSymbol var = StmtHandler(node,table);
					if(var != null){
						if(var.getId() != null && var.getId().length() > 0){ //子节点为 VarDecl
							PushVarIntoTable(var,vars,table);
						}
						else{	//字节点为return，判断返回类型是否正确
							if(return_type==null)
								return_type = var.getType();
							else
							{
								if(!return_type.equals(var.getType())){
									exceptions.add(new SemanticsException(SemanticsException.RETURN_TYPE_ERROR,
											"Different Return Type",node.jjtGetLine()));
								}
							}
						}
					}
				}
				break;
			}
		}
		
		PopVarsFromTable(vars,table);
		if (return_type == null)
			return null;
		VarSymbol result = new VarSymbol();
		result.setType(return_type);
		result.setId("");
		result.setLine(whileNode.jjtGetLine());
		result.setJJTType(XYZ2parserTreeConstants.JJTRETURNSTATEMENT);
		return result;
	}
	
	public void PrintStmtHandler(SimpleNode printNode, Table table){
		Node[] children = printNode.getChildren();
		SimpleNode child = (SimpleNode)children[2];
		String type = ExpHandler(child,table);
		if(!type.equals("boolean") && !type.equals("int") && !type.equals("long")) {
			exceptions.add(new SemanticsException(SemanticsException.PRINT_TYPE_ERROR,
					"print object illegal",printNode.jjtGetLine()));
		}
	}
	
	public void AssignStmtHandler(SimpleNode assignNode, Table table){
		Node[] children = assignNode.getChildren();
		VarSymbol var = table.getVarSymbol(((ASTMyID)children[0]).getName());
		if (var == null) {
			exceptions.add(new SemanticsException(SemanticsException.VAR_UNDEFINED,
					"variable undefined",assignNode.jjtGetLine()));
			return;
		}
		SimpleNode child = (SimpleNode)children[2];
		String type = ExpHandler(child,table);
		if (!CanT1BeT2(var.getType(),type,table)) {
			exceptions.add(new SemanticsException(SemanticsException.DIFFERENT_TYPE_ASSIGN,
					"assign type not match",assignNode.jjtGetLine()));
		}
	}
	
	public void ArrayAssignStmtHandler(SimpleNode arrayAssignNode, Table table){
		Node[] children = arrayAssignNode.getChildren();
		SimpleNode child = (SimpleNode)children[2];
		String type = ExpHandler(child,table);
		if (!type.equals("int") && !type.equals("long")) {
			exceptions.add(new SemanticsException(SemanticsException.INDEX_TYPE_ERROR,
					"index type error",arrayAssignNode.jjtGetLine()));
		}
		VarSymbol var = table.getVarSymbol(((ASTMyID)children[0]).getName());
		if (var == null) {
			exceptions.add(new SemanticsException(SemanticsException.VAR_UNDEFINED,
					"variable undefined",arrayAssignNode.jjtGetLine()));
			return;
		}
		String tmp = var.getType();
		if ((!tmp.equals("int[]")) && (!tmp.equals("long[]"))) {
			exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
					"type not match",arrayAssignNode.jjtGetLine()));
			return;
		}
		
		type = ExpHandler((SimpleNode)children[5],table);
		if (tmp.equals("int[]"))
			tmp = "int";
		else
			tmp = "long";
		if (!CanT1BeT2(tmp,type,table)) {
			exceptions.add(new SemanticsException(SemanticsException.DIFFERENT_TYPE_ASSIGN,
					"assign type not match",arrayAssignNode.jjtGetLine()));
		}
	}
	
	public VarSymbol ReturnStmtHandler(SimpleNode returnNode, Table table){
		Node[] children = returnNode.getChildren();
		VarSymbol result = new VarSymbol();
		result.setType(ExpHandler((SimpleNode)children[1],table));
		result.setId("");
		result.setLine(((SimpleNode)children[1]).jjtGetLine());
		result.setJJTType(XYZ2parserTreeConstants.JJTRETURNSTATEMENT);
		return result;
	}
	
	public String ExpHandler(SimpleNode expNode, Table table){	// return type
		Node[] children = expNode.getChildren();
		if (children.length == 1) {
			return AndExpHandler((SimpleNode)children[0],table);
		}
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTANDEXP:{	 
					if(!AndExpHandler(node,table).equals("boolean")){
						exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
								"OR Expression type not boolean",node.jjtGetLine()));
						return "exp_error";
					}
				}
				break;
			}
		}
		return "boolean";
	}
	
	public String AndExpHandler(SimpleNode expNode, Table table){
		Node[] children = expNode.getChildren();
		if (children.length == 1) {
			return CompExpHandler((SimpleNode)children[0],table);
		}
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTCOMPEXP:{	 
					if(!CompExpHandler(node,table).equals("boolean")){
						exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
								"AND Expression type not boolean",node.jjtGetLine()));
						return "exp_error";
					}
				}
				break;
			}
		}
		return "boolean";
	}
	
	public String CompExpHandler(SimpleNode expNode, Table table){
		Node[] children = expNode.getChildren();
		if (children.length == 1) {
			return AddExpHandler((SimpleNode)children[0],table);
		}
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTADDEXP:{
					String type = AddExpHandler(node,table);
					if(!type.equals("int") && !type.equals("long")){
						exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
								"COMPARE Expression type not int or long",node.jjtGetLine()));
						return "exp_error";
					}
				}
				break;
			}
		}
		return "boolean";
	}
	
	public String AddExpHandler(SimpleNode expNode, Table table){
		Node[] children = expNode.getChildren();
		if (children.length == 1) {
			return TimesExpHandler((SimpleNode)children[0],table);
		}
		String result = "int";
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTTIMESEXP:{
					String type = TimesExpHandler(node,table);
					if (type.equals("long")) {
						result = "long";
					}
					if(!type.equals("int") && !type.equals("long")){
						exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
								"ADD Expression type not int or long",node.jjtGetLine()));
						return "exp_error";
					}
				}
				break;
			}
		}
		return result;
	}
	
	public String TimesExpHandler(SimpleNode expNode, Table table){
		Node[] children = expNode.getChildren();
		if (children.length == 1) {
			return BaseExpHandler((SimpleNode)children[0],table);
		}
		String result = "int";
		for(int i = 0; i < children.length; ++i){
			SimpleNode node = (SimpleNode)children[i];

			switch(node.getId()){
				case XYZ2parserTreeConstants.JJTBASEEXP:{
					String type = BaseExpHandler(node,table);
					if (type.equals("long")) {
						result = "long";
					}
					if(!type.equals("int") && !type.equals("long")){
						exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
								"TIMES Expression type not int or long",node.jjtGetLine()));
						return "exp_error";
					}
				}
				break;
			}
		}
		return result;
	}
	
	public String BaseExpHandler(SimpleNode expNode, Table table){
		Node[] children = expNode.getChildren();
		switch(((SimpleNode)children[0]).getId()){
			case XYZ2parserTreeConstants.JJTMYOP:
				return BaseExpHandler((SimpleNode)children[1],table);
			case XYZ2parserTreeConstants.JJTPRIMEEXP:{
				//String result = "exp_error";
				String type = PrimeExpHandler((SimpleNode)children[0],table);
				for (int i = 1; i < children.length; ) {
					if (((SimpleNode)children[i]).getId() == XYZ2parserTreeConstants.JJTMYSYMBOL) {
						if (!type.equals("int[]") && !type.equals("long[]")) {
							exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
									"Illegal array error",((SimpleNode)children[i]).jjtGetLine()));
							return "exp_error";
						}
						//result = type;
						String tmp = ExpHandler((SimpleNode)children[i+1],table);
						if (!tmp.equals("int") && !tmp.equals("long")) {
							exceptions.add(new SemanticsException(SemanticsException.INDEX_TYPE_ERROR,
									"Index type error",((SimpleNode)children[i+1]).jjtGetLine()));
							return "exp_error";
						}
						if (type.equals("int[]"))
							type = "int";
						else
							type = "long";
						i += 3;
					}
					else {
						if (((SimpleNode)children[i+1]).getId() == XYZ2parserTreeConstants.JJTMYID) {
							Object[] msList;
							MethodSymbol ms = null;
							if (type.equals("this")) {
								msList = table.getAllMethods(((ASTMyID)children[i+1]).getName());
							}
							else {
								ClassSymbol cs = table.getClassSymbol(type);
								if(cs == null)
									return "exp_error";
								if(cs.getAllMethod(((ASTMyID)children[i+1]).getName()) == null)
									return "exp_error";
								msList = cs.getAllMethod(((ASTMyID)children[i+1]).getName());
							}
							//check explist 重载的问题
							String[] expList = ExpListHandler((SimpleNode)children[i+3],table);
							for(int j = 0; j < expList.length; j++){
								if (expList[j].equals("exp_error"))
									return "exp_error";
							}
							for(int j = 0; j < msList.length; j++){
								VarSymbol[] params = ((MethodSymbol)msList[j]).getParams();
								if (params.length != expList.length)
									continue;
								int k;
								for(k = 0; k < params.length; k++){
									if(!CanT1BeT2(params[k].getType(),expList[k],table))
									{
										break;
									}
										
								}
								if (k == params.length){
									ms = (MethodSymbol)msList[j];
									break;
								}
							}
							if (ms == null){
									exceptions.add(new SemanticsException(SemanticsException.PARAMETER_PASS_ERROR,
											" type not match",((SimpleNode)children[i+1]).jjtGetLine()));
								return "exp_error";
							}
							type = ms.getType();
							i += 5;
						}
						else {
							if (!type.equals("int[]") && !type.equals("long[]")) {
								exceptions.add(new SemanticsException(SemanticsException.TYPE_MATCH_ERROR,
										"not array error",((SimpleNode)children[i]).jjtGetLine()));
								return "exp_error";
							}
							//result = "int";
							type = "int";
							i += 2;
						}
					}
				}
				return type;
			}
		}
		return "exp_error";
	}
	
	public String[] ExpListHandler(SimpleNode expListNode, Table table){
		Node[] children = expListNode.getChildren();
		String[] result = new String[children.length];
		result[0] = ExpHandler((SimpleNode)children[0],table);
		for (int i = 1; i < children.length; i++){
			Node[] restNode = ((SimpleNode)children[i]).getChildren();
			result[i] = ExpHandler((SimpleNode)restNode[1],table);
		}
		return result;
	}
	public String PrimeExpHandler(SimpleNode expNode, Table table){	// finished
		Node[] children = expNode.getChildren();
		SimpleNode node = (SimpleNode)children[0];
		switch(node.getId()){
			case XYZ2parserTreeConstants.JJTMYID:{
				VarSymbol var = table.getVarSymbol(((ASTMyID)node).getName());
				if (var == null) {
					exceptions.add(new SemanticsException(SemanticsException.VAR_UNDEFINED,
							"variable undefined",node.jjtGetLine()));
					return "exp_error";
				}
				return var.getType();
			}
			case XYZ2parserTreeConstants.JJTMYINT:{
				return "int";
			}
			case XYZ2parserTreeConstants.JJTMYLONG:{
				return "long";
			}
			case XYZ2parserTreeConstants.JJTMYSYMBOL:{
				return ExpHandler((SimpleNode)children[1],table);
			}
			case XYZ2parserTreeConstants.JJTMYKEYWORD:{
				String type_name = ((ASTMyKeyword)node).getName();
				if(type_name.equals("true")||type_name.equals("false"))
					return "boolean";
				if(type_name.equals("this"))
					return "this";
				if(type_name.equals("new")){
					if (((SimpleNode)children[1]).getId() == XYZ2parserTreeConstants.JJTMYID) {
						return ((ASTMyID)children[1]).getName();
					}
					else {
						String type = ExpHandler((SimpleNode)children[3],table);
						if (!type.equals("int") && !type.equals("long")) {
							exceptions.add(new SemanticsException(SemanticsException.INDEX_TYPE_ERROR,
									"index type error",((SimpleNode)children[3]).jjtGetLine()));
							return "exp_error";
						}
						return ((ASTMyKeyword)children[1]).getName()+"[]";
					}
				}
			}
		}
		return "exp_error";
	}
	
	private boolean CanT1BeT2(String t1, String t2, Table table){
		if(t1.equals(t2))
			return true;
		
		else if(t1.equals("long") && t2.equals("int"))
			return true;
		
		else{
			ClassSymbol cs1 = table.getClassSymbol(t1);
			ClassSymbol cs2 = table.getClassSymbol(t2);
			if(cs1 == null || cs2 == null)
				return false;
			
			else if(t1.equals(cs2.getSuper())){
				return true;
			}
		}
		return false;
	}
	
	/*  0 不等
	 *  1 重载
	 *  2 相同
	 *  3 出错
	 */
	private int CompareMethods(MethodSymbol ms1, MethodSymbol ms2){
		if(ms1.getId().equals(ms2.getId()) == false)
			return 0;
		
		//以下两种情况为 函数名一样，但参数列表不同
		VarSymbol[] params1 = ms1.getParams();
		VarSymbol[] params2 = ms2.getParams();
		if(params1.length != params2.length)
			return 1;
		
		for(int i = 0; i < params1.length; ++i){
			if(params1[i].getType().equals(params2[i].getType()) == false)
					return 1;
		}
		if(ms1.getType().equals(ms2.getType()))
			return 2;
		else
			return 3;
	}
}